{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T06:56:58.697710Z",
     "start_time": "2024-08-27T06:56:58.681843Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\XY-33\\AppData\\Roaming\\Python\\Python312\\site-packages\\pydantic\\_internal\\_fields.py:132: UserWarning: Field \"model_kwargs\" in QianfanEmbeddingsEndpoint has conflict with protected namespace \"model_\".\n",
      "\n",
      "You may be able to resolve this warning by setting `model_config['protected_namespaces'] = ()`.\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "#!/usr/bin/env python3\n",
    "# coding=utf-8\n",
    "\n",
    "\"\"\"\n",
    "Baidu Qianfan: https://qianfan.cloud.baidu.com/\n",
    "\"\"\"\n",
    "\n",
    "import os\n",
    "from typing import Union\n",
    "\n",
    "from langchain_community.chat_models import QianfanChatEndpoint\n",
    "from langchain_community.embeddings import QianfanEmbeddingsEndpoint\n",
    "from langchain_community.llms import QianfanLLMEndpoint\n",
    "from langchain_core.embeddings.embeddings import Embeddings\n",
    "from langchain_core.language_models import BaseLanguageModel, BaseChatModel\n",
    "from langchain_core.messages import BaseMessage\n",
    "\n",
    "# https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Nlks5zkzu\n",
    "# INSTRUCT_MODEL = 'ERNIE-Speed-8K'\n",
    "INSTRUCT_MODEL = 'ERNIE-4.0-Turbo-8K'\n",
    "# INSTRUCT_MODEL = 'ERNIE-3.5-8K'\n",
    "CHAT_MODEL = INSTRUCT_MODEL\n",
    "EMBEDDINGS_MODEL = 'bge-large-zh'\n",
    "\n",
    "common_options = {\n",
    "    'qianfan_ak': '',\n",
    "    'qianfan_sk': ''\n",
    "}\n",
    "\n",
    "_llm, _chat_llm, _embeddings = None, None, None\n",
    "\n",
    "\n",
    "def create_llm(**kwargs) -> BaseLanguageModel[Union[str, BaseMessage]]:\n",
    "    \"\"\"create `QianfanLLM`, can be used to replace `OpenAI`\"\"\"\n",
    "    global _llm\n",
    "\n",
    "    if len(kwargs) == 0:\n",
    "        if _llm is None:\n",
    "            _llm = QianfanLLMEndpoint(model=INSTRUCT_MODEL, **common_options)\n",
    "        return _llm\n",
    "\n",
    "    options = {'model': INSTRUCT_MODEL, **common_options, **kwargs}\n",
    "    return QianfanLLMEndpoint(**options)\n",
    "\n",
    "\n",
    "def create_chat_llm(**kwargs) -> BaseChatModel:\n",
    "    \"\"\"create `QianfanChat`, can be used to replace `ChatOpenAI`\"\"\"\n",
    "    global _chat_llm\n",
    "\n",
    "    if len(kwargs) == 0:\n",
    "        if _chat_llm is None:\n",
    "            _chat_llm = QianfanChatEndpoint(model=CHAT_MODEL, **common_options)\n",
    "        return _chat_llm\n",
    "\n",
    "    options = {'model': CHAT_MODEL, **common_options, **kwargs}\n",
    "    return QianfanChatEndpoint(**options)\n",
    "\n",
    "\n",
    "def create_embeddings(**kwargs) -> Embeddings:\n",
    "    \"\"\"create `QianfanEmbeddings`, can be used to replace `OpenAIEmbeddings`\"\"\"\n",
    "    global _embeddings\n",
    "\n",
    "    if len(kwargs) == 0:\n",
    "        if _embeddings is None:\n",
    "            _embeddings = QianfanEmbeddingsEndpoint(model=EMBEDDINGS_MODEL, **common_options)\n",
    "        return _embeddings\n",
    "\n",
    "    options = {'model': EMBEDDINGS_MODEL, **common_options, **kwargs}\n",
    "    return QianfanEmbeddingsEndpoint(**options)\n",
    "\n",
    "\n",
    "creators = (create_llm, create_chat_llm, create_embeddings)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T06:57:31.141365Z",
     "start_time": "2024-08-27T06:57:31.134573Z"
    },
    "id": "jPIRSGz4tHNV"
   },
   "outputs": [],
   "source": [
    "from langchain_core.runnables import  RunnablePassthrough\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "import os\n",
    "import json\n",
    "from langchain_community.graphs import Neo4jGraph\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "from langchain_experimental.graph_transformers import LLMGraphTransformer\n",
    "from neo4j import GraphDatabase\n",
    "from yfiles_jupyter_graphs import GraphWidget\n",
    "from langchain_community.vectorstores import Neo4jVector\n",
    "from langchain_community.document_loaders import TextLoader\n",
    "from langchain_community.vectorstores.neo4j_vector import remove_lucene_chars"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## GraphRAG"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T06:57:39.372619Z",
     "start_time": "2024-08-27T06:57:35.157979Z"
    },
    "id": "L0nXP1aYtHNW",
    "notebookRunGroups": {
     "groupValue": "1"
    }
   },
   "outputs": [],
   "source": [
    "graph = Neo4jGraph(url=\"bolt://localhost:7687\", username=\"neo4j\", password=\"12345678\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 加载文档"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T07:13:31.493165Z",
     "start_time": "2024-08-27T07:13:31.456547Z"
    },
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "sGhtLTAStHNW",
    "outputId": "39f65561-6c6b-4370-a51f-1d8118591158"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='王志纲年度报告2021\\n发刊词\\n实战派的战略思维 第一讲\\n硬科技发力：为什么新机会反而在中西部？\\n.......9 第二讲\\n协作共赢：民营企业的机会究竟在哪？\\n.16 第三讲')]\n",
      "109\n",
      "[Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='王志纲年度报告2021\\n发刊词\\n实战派的战略思维 第一讲\\n硬科技发力：为什么新机会反而在中西部？\\n.......9 第二讲\\n协作共赢：民营企业的机会究竟在哪？\\n.16 第三讲'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='.16 第三讲\\n卖生活：房产企业怎么才能活下去？\\n23 第四讲\\n公平优先：为什么“双减”不是偶然的政策变量？\\n30 第五讲\\n老而不衰：为什么老龄化是个商业问题？\\n36 第六讲'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='36 第六讲\\n家风胜万贯：为什么要关注民营企业二代传承问题？\\n...42 第七讲\\n好人赚钱：为什么共同富裕不只是要分蛋糕？\\n.49 第八讲\\n大国博弈：为什么碳中和不只是环保问题？'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='55 第九讲\\n算经济账：为什么要从成本博弈看中美关系？\\n.61 第十讲\\n个人定位：为什么内卷不是时代的错？\\n68 发刊词|实战派的战略思维'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='你好，我是王志纲，欢迎来到我的《年度报告2021》。我先简单做一个自我介绍，我是智纲智库的创始人，主要做战略咨询工作。作为一个50后，我有可能是得到上年龄最大的老师。为什么要由我'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='的老师。为什么要由我来为你提供这样一份报告呢？当然不是因为我年纪大，不过，多少与我的人生阅历有关。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='1.为什么由我来做这个报告\\n90年代初，我从记者转型做战略咨询以来，将近三十年时间，大大小小，做过不下上千个项目，涵盖地产、文旅、区域经济、制造业、大消费乃至大健康等各个领域。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='这么多年以来，从东部到西部，从沿海到沿江，从发达地区到闭塞山区，我几乎参与到了中国各大主要城市和区域的战略制定中去。同时我也有幸参与了许多企业的转型和战略制定，见证了他们发展的关'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='，见证了他们发展的关键时期。比如茅台、碧桂园、万达、龙湖地产等。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='所以说和大学、商学院里面的教授相比，我是彻头彻尾的实战派。我做这个报告的初心，就是想把自己吃饭的家伙，也就是看问题，看世界和解决问题的方法告诉你，希望你能更好的去做自己的战略规划'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='的去做自己的战略规划，把握自己的人生航向。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='我经常做一个比喻，形容战略和管理的区别，同样是种树，管理是修剪枝丫，浇水施肥等日常操作，战略要回答的则是“为什么种树”“种一棵什么树”“在哪里种树”“要不要种树”，这些方向性、原'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='树”，这些方向性、原则性的大问题。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='多年的咨询实践，让我见过太多成败得失，也让我形成了自己的观察视角，这个视角就是战略思维。我认为，战略的本质是在关键时刻，做好重大抉择。那么有了战略思维，就能找到关键时期的关键变量'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='到关键时期的关键变量，去做正确的事和正确地做事，使自己的人生更加精彩。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='如果你还是觉得战略思维这个词有点虚，你也可以理解为，它就是一把“解牛刀”，它能帮你穿透表层，看问题更清晰、更透彻。我在给很多企业、地方政府做服务时，就是用的这把“解牛刀”。当然，'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='把“解牛刀”。当然，这把“解'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='牛刀”有很多用法，我认为你最需要掌握的一种，就是去寻找小道理背后的大道理，这一条。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='在纷繁复杂的世界中，总会有一些凌驾于其他道理之上的大道理。抓住大道理，才能抓住影响全局的关键变量。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='举个例子，我现在依然记得，我和碧桂园的第一次合作。1993年，中国的地产市场遭遇了狂飙突进以来，最大的一次宏观调控，惨烈程度远胜于今天的这种状态。当时正在高速运转，狂飙突进的房地'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='运转，狂飙突进的房地产市场在中央一夜之间紧急刹车，收紧银根的宏观调控下戛然而止，接下来是哀鸿遍野，楼盘全面失火。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='大难临头，路在何方？这就需要战略了。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='在这个时候，碧桂园的老板找上了门。我们经过了深入的探讨和诊断以后，一拍即合。我们合作的结果有目共睹，我就不多说了，产生了一个非常大的奇迹。但背后的奥秘是啥呢？其实体现的就是一个小'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='其实体现的就是一个小道理，必须服从大道理。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='这个大道理是啥呢？第一，这个宏观调控不是暂时的，而是必然的。也就是这个严冬已经来了，你如果心存侥幸，以为这是个暂时的，熬一熬就过去，那么你肯定会死掉。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='第二个，路在何方呢？我们就找到了一种“反弹琵琶”“围魏救赵”的方式。当时珠三角已经开始有了一批先富起来的人群，他们最难解决的'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='是生意和孩子的教育和发展，两者兼顾。所以找到了这个痛点，最后通过办一个所谓的贵族学校，用教育储备金的方式解决了资金的问题。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='第三，后来碧桂园手里有了10亿、8亿现金以后，要给大家营造了一种美好的生活，就是所谓“给你一个五星级的家”。你们不仅可以把自己的孩子高枕无忧放到这里，而且把自己的家和自己未来期望'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='己的家和自己未来期望的生活也托付到这里。所以这个项目不成功都不可能，这个项目到今天已经二三十年了。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='我们如果回头看它的奥秘是什么？就是小道理必须服从大道理。抓住了大道理，其他的问题就可以迎刃而解。如果抓不到大道理，而是就事论事，或者是心存侥幸，死亡只是早晚的问题。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='2.我将怎么做这份报告\\n在今天这个变局时代更需要这种战略思维。中国经济正面临上下半场的转型，很多上半场通行的规则在下半场都不再适用。大道理在变，小道理自然要跟着变。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='比如今年教培行业发生了巨大的变化，但如果你能洞悉大道理，就知道这是国家重视教育公平的必然结果，那你的策略就要跟着变，去教育平权里找机遇。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='再比如多年以来，房产行业的游戏规则就是高杠杆、快周转、金融化但如果你能洞悉大道理，就知道在未来，这个行业的民生属性一定会加强，那你就要去那里找机遇，转型成生活方式的提供者。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='今年还有很多民企的发展也遇到了困境，有的还被当地国资委接盘，但如果你能洞悉大道理，就知道在未来，民企如果能发挥服务、内容方面的优势，恰恰能与国企互补，从而抓住自己的机遇。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='找到小道理背后的大道理，这就是我做这份报告采用的观察方法。在这份报告中，我可能无法给你太多的知识，因为这不是我的强项，而且也有无数专家学者能给你提供丰富的知识了。我能给你的，就是'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='了。我能给你的，就是我在自己的工作中，用的那把“解牛刀”，说得高大上一点，就是战略思维。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='这份报告中，我为你挑选了过去一年有重大影响，也很能反映时代变局的十大议题。其中有区域发展、中美博弈、共同富裕等宏观问题，也有房地产、教育、碳中和等行业问题，还有老龄化、二代传承、'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='有老龄化、二代传承、内卷等社会性问题。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='我是如何用战略思维，这把“解牛刀”来分析这些问题的呢？'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='举个例子，当中国经济向高质量转型，这会引发一些大道理的变化。比如，经济引擎正在从之前的外贸、房地产、金融等变成硬科技。当这个大道理变了之后，城市发展的小道理也会发生变化。比如，未'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='会发生变化。比如，未来经济增长最有潜力的地方，有可能不再是南方沿海，而是在中西部地区。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='在这份报告里，你还能看到很多这样的变化。美国电影《教父》中有一句话，一个一秒钟就能看透事情本质的人，和一个半辈子都看不透的人，人生是天差地别的。在我看来，有了战略思维，至少你能看'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='战略思维，至少你能看透这十件大事，从台前抵达幕后，从偶然抵达必然，从现象抵达本质。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='3.哪些人需要这份报告？\\n最后，我想谈谈哪些人可能更需要这份报告。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='如果你希望了解未来的政策变量，指导自己事业发展方向，我会从大道理如何影响小道理的角度，讲一讲我看到的一些趋势，比如碳中和、养老产业的机遇等。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='如果你是教培、房地产从业者，或其他正经历转型阵痛的企业经营者，希望看懂当下的变局，为下一步转型寻找方向。我也会给你提一些建议，比如房地产回归民生议题后的转型方向，民营企业如何与国'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='向，民营企业如何与国企合作等。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='如果你刚入职场，或者想换个城市，换个工作，通过这个报告，你可以更明白如何去做适合自己的选择，找到自己的定位。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='中国有一句古话，风起于青萍之末。什么意思？就是重要的不是趋势，而是趋势的转变。现在中国正处在一个大变局中，要看到这种转变非常'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='重要。希望这份报告能够给你一些启发，用战略思维去思考问题，去抓住那些影响未来的大道理，找到自己的航标。\\n我是王志纲，咱们课程里见。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='《王志纲年度报告·2021》\\n发刊词实战派的战略思维\\n01|硬科技发力：为什么新机会反而在中部？\\n02|协作共赢：民营企业的机会究竟在哪？\\n03卖生活：房产企业怎么才能活下去？'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='04|公平优先：为什么“双减”不是偶然的政策变量？\\n05老而不衰：为什么老龄化是个商业问题？\\n06|好人赚钱：为什么共同富裕不只是要分蛋糕？'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='07大国博弈：为什么碳中和不只是环保问题？\\n08|算经济帐：为什么要用成本视角看中美博弈？\\n09|家风胜万贯：为什么要关注民营企业二代传承问题？'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='10|个人定位：为什么内卷不是时代的错？\\n《王志纲年度报告·2021》\\n-得到App出品-'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='第一讲|硬科技发力：为什么新机会反而在\\n中西部？\\n你好，我是王志纲，这一讲我带你关注的话题是未来中国经济的增长亮点在哪里。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='说起区域经济，很多人一直都有“南强北弱”“南快北慢”的感觉。从数据上看，北方城市的表现也确实不如南方。比如今年前三季度，GDP前十的城市中，也只有北京一个北方城市。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='以至于有许多有理想想闯出一番事业的人，常常盯着东南沿海，尤其是珠三角和长三角。甚至不少经济学家也在呼吁“向南、向南！”'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='但在我看来，在一边倒地对沿海经济的看好中，不能忽略一个新的增长苗头。也就是经过了三十多年的梯度发展，下一阶段终于轮到中西部发力的时候了。也许，中西部城市的崛起将会成为中国经济增长'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='将会成为中国经济增长的新亮点。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='为什么这么说呢？这一讲我们就来看看。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='1.很多人说南方好\\n哪里经济发展好，机会多，人才就愿意往哪里去，这是规律。很多大学生毕业后想去南方城市找工作。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='我们先看一下2019年的数据，北方所有省份的GDP加起来只占35%，而南方达到了65%。不但中国人看好南方，外国人也一样。2021年，英国知名的《经济学人》，按照自己的标准，评选'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='按照自己的标准，评选出了中国最具经济潜力城市。其中，排名前十的城市，除了北京，全都是南方城市。杭州位列榜首，紧接着是深圳、广州、上海、珠海。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='这样的排名一出来，会增加很多人向南、向南的决心。不过，真的要一路向南吗？在我看来，在当下这个节骨眼上，你确实要好好考虑一下。\\n2.为什么不要盯着南方了？'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='你心里一直有南方经济更强的感觉，其实是一种思维惯性。我们之所以有这种思维惯性与改革开放从南方开始有关。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='我一直有这么一个观点，在农耕文明的时候，平原最值钱。在工业文明的时候，沿海最值钱。为什么工业文明的时候，沿海最值钱呢？'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='沿海地区通江达海，物流、人力成本低，正好承接了来自亚洲四小龙转移来的劳动密集型产业。所以，从80年代初开始，我国就逐渐形成了“两头在外，大进大出”的商业模式。比如广东的东莞等，从'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='比如广东的东莞等，从衣服、鞋'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='帽这些低端的轻工产品开始。一路走来，40年一次一次的迭代，一次一次的升级，终于变成了全球著名的世界工厂。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='就在这个过程中，东南沿海地区的民营经济如雨后春笋般涌现。不仅大量制衣厂、电子厂吸引了大量农民工，这里还成为冒险家的乐园。全世界的华人、全中国想有一番作为的野心家，纷纷来这里淘金。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='家，纷纷来这里淘金。最后他们在改变自己命运的同时，也支撑起这个地方的持续繁荣。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='在八九十年代，我还在做记者时，曾经采访过很多内地省市的领导。面对“一江春水向东流”“孔雀东南飞”，这些以阻档的局面，他们也表现得非常无奈。我印象特别深刻，有一次采访湖北省委书记，'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='次采访湖北省委书记，他这样自嘲。他说，今天不仅是孔雀东南飞，甚至麻雀也东南飞，挡都挡不住。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='现在我国的劳动力优势也不再那么明显了，全球化带来的产业转移已经不可持续。以广东省为例，在1994年对外贸易的比重约占到全国的41%，是历史最高位。“东西南北中，发财向广东。”广东'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='，发财向广东。”广东的确是整个中国的超级明星。到今年前8个月，广东的外贸占比已经降到了21%。从全国看，2012年，我国的外贸依存度占到45%，但是，到2020年，已经降到32%。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='好，除了外贸动力不足以外，我说不要一直盯着南方找机会还有另一个原因。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='你需要注意到，曾经拉动南方经济再次起飞的互联网、房地产和金融行业，已经大不如从前，增长空间极度被压缩。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='曾经，一些互联网巨头企业在讯息发达的沿海地区发展起来，很多拿着一台电脑就可以打天下的牛人，只要有好的商业模式，有资本支持，就能改变世界。但是进入智能化时代，经济的核心动力变成硬科'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='济的核心动力变成硬科技，模式创新难以为继。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='什么是硬科技？就是与实体产业息息相关的核心底层技术。比如光电芯片、人工智能、生物技术、新材料、新能源、智能制造等。这些企业不再依赖原料和产品的大规模进出口，而是要靠大量的专业人才'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='是要靠大量的专业人才和技术积累，以及有耐心的资本。而南方资本已经有了路径依赖，习惯了赚快钱，赚大钱的生意。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='所以，从长远看，南方沿海地区将失去传统优势，在硬科技上长期投入不是它们的强项。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='那未来要找发展机会应该看哪里？我认为，应该跳出南北，看中西部。需要注意的是，我这里说的中西部，既有典型的北方省份河南，又包括南方省份安徽、湖北，还有西部的巴蜀等，也就是四川、重庆'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='等，也就是四川、重庆等。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='中西部的优势在哪里呢？下面我们来分析一下。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='3.为什么机会在中西部？\\n好，我们刚才说，未来能支撑起中国经济的企业，大多是硬科技企业。\\n当拉动经济发展的引擎在变，要发展这类企业，中西部优势更为明显。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='第一，在科研人才方面，沿海地区高校和科研院所有限。这些年，这些地方之所以表现出人才济济，主要靠的是吸引中西部的科技人员。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='人才储备不足，同时生活成本高，会越来越难吸引人才。反之，西安、武汉、合肥、郑州等中西部城市早在改革开放以前，就是我们国家重点投入的教育和科技重镇，本身就拥有丰富的科技人才资源，硬'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='富的科技人才资源，硬科技企业也可以“就地取材”。比如武汉，这里有130多万的在校大学生，不仅在中国，甚至在全世界就规模来说，也是名副其实的大学之城。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='第二，在产业基础方面，由于在外贸经济、互联网经济热潮中落后，中西部省份反而更有动力抓住硬科技这根稻草，后发制人。不少中西部城市的政府，积极化身风投，引入硬科技企业。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='比如合肥，多少年来一直是一个默默无闻的省会城市，而今天却大出风头，成了明星城市。科大讯飞也好，蔚来汽车也黑，甚至是还半蒙着面纱的量子科学，未来都有很大的潜力。在这样的背景下，合肥'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='在这样的背景下，合肥也就有底气让城市成为最大的创投公司来扶持高新科技企业，迅速地实现了弯道超车。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='第三，中西部地区有良好的高端制造业基础，这是中国经济走向强大的重要力量。比如合肥在量子通信、动态储存芯片、显示玻璃等领域都出'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='现了领跑企业，武汉的特种光纤企业填补了自主产业链的上游空白等等。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='因此，在这些基础的带动下，中西部的崛起将是一个大概率事件，也是值得我们高度关注的一个区域现象。如果没有例外，这将是未来中国十年区域发展的主旋律、大趋势。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='同时我还有一个判断，未来十年中西部地区将会涌现出更多的新一线城市。大批的人才背井离乡，将会成为历史。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='今年我去武汉考察时，感受就很强烈。我在和腾讯、华为武汉的一些高管交流的时候，打听他们毕业的学校居然大多数是华科的。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='我是个文科男，不太知道理工科，我说华科是谁，他们说就是华中科技大学。我说华中科技大学不就是孟晚舟的学校。他们说不仅是孟晚舟的学校，腾讯的“微信之父”张小龙也是华科毕业的。后来往下'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='华科毕业的。后来往下延展，我发现整个腾讯和华为的很多高端人才都是华科的。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='后来我又问他们一句话，为什么你们不选择去北上广深的总部呢？他们说，他们搞了个内部调查，问现在的这些大学生，愿意选择什么地方。他们首选的不是深圳、北京、上海，而是成都、武汉。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='第一，这里有机会。第二，这里房价便宜。第三，这里结得起婚，还能赡养父母。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='一句话，当你要寻找幸福指数的时候，这里比北上广深都强。在这种背景下面，逼着它们的总部也开始把自己的重心往中部转移，或者建立新的总部，或者建立第二总部，倒过来，将就人才。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='千万不要小看这种现象，这是一个历史大趋势的重新开始。也就是后来我总结的，40年前是农民“背井离乡”，而今天你们是新的“离土不离乡”，再不“背井离乡”，这是一个历史性的大转换。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='最后，我还要补充一点，支撑中西部发展的一个重要原因，就是消费拉动的作用在变大。消费现在是拉动中国增长的主要动力，再加上内需时代的到来，中部人口众多，成为基数最大、活力最强的消费市'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='大、活力最强的消费市场。这些沿海的大企业自然纷纷抢滩中部。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='那么，这个变化对你有什么意义呢？我想，无论你是想创业，还是找工作，不用再一头扎进北上广深这些一线大城市了。我建议你可以关注中西部区域的中心城市，特别是大都市圈的发展。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='同时，我建议你在做抉择时，要从多元维度思考。我常说一句话，人活着都是在追求“三生有幸”，生活、生命、生意，一个都不能少。现在要想在北上广深同时实现“三生有幸”，其实很难。但是，如'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='，其实很难。但是，如果要在武汉、合肥、郑州这样的中部城市，“三生有幸”是一个可以触达的人生目标。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='好，我是王志纲，我们下一讲再见。')]\n"
     ]
    }
   ],
   "source": [
    "loader = TextLoader(file_path=\"20240920_7_王志纲年度报告·2021-页面-1_1.txt\")\n",
    "docs = loader.load()\n",
    "\n",
    "text_splitter = RecursiveCharacterTextSplitter(chunk_size=90, chunk_overlap=10)\n",
    "documents = text_splitter.split_documents(documents=docs)\n",
    "print(documents[0:1])\n",
    "print(len(documents))\n",
    "print(documents)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.document_loaders import CSVLoader\n",
    "\n",
    "loader = CSVLoader(file_path=\"get_relation_data.csv\")\n",
    "documents = loader.load()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 建立向量数据库"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "faiss 检索\n",
    "pip install faiss-cpu\n",
    "\n",
    "感觉 faiss 向量化要快一些"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Document(metadata={'source': 'get_relation_data.csv', 'row': 3}, page_content='output: 养老观念 - BELONGS_TO -> 中国人'), Document(metadata={'source': 'get_relation_data.csv', 'row': 4}, page_content='output: 养老方式 - BELONGS_TO -> 中国人')]\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.vectorstores import FAISS\n",
    " \n",
    "\n",
    "# 加载本地 embedding 模型\n",
    "embedding =create_embeddings()\n",
    "# 创建向量数据库\n",
    "db = FAISS.from_documents(documents, embedding)\n",
    "# 保存\n",
    "db.save_local(\"./faiss_index\")\n",
    "'''\n",
    "如果已经创建好了，可以直接读取\n",
    "db = FAISS.load_local(\"./faiss_index\", embeddings)\n",
    "'''\n",
    " \n",
    "# 直接传入文本\n",
    "query = \"人到老年\"\n",
    "docs = db.similarity_search(query, k=3)\n",
    "print(docs[0].page_content)\n",
    " \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='有老龄化、二代传承、内卷等社会性问题。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='到关键时期的关键变量，去做正确的事和正确地做事，使自己的人生更加精彩。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='的去做自己的战略规划，把握自己的人生航向。')]\n"
     ]
    }
   ],
   "source": [
    "# 传入向量去搜索\n",
    "embedding_vector = embedding.embed_query(query)\n",
    "docs = db.similarity_search_by_vector(embedding_vector, k=3)\n",
    "print(docs[0].page_content)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "带分数的top k"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='有老龄化、二代传承、内卷等社会性问题。'), 0.49693966)\n"
     ]
    }
   ],
   "source": [
    "docs = db.similarity_search_with_score(query, k=3)  # 带分数的\n",
    "print(docs[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='和、养老产业的机遇等。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='作为一个50后，我有可能是得到上年龄最大'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='年纪大，不过，多少与我的人生阅历有关。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='-得到App出品-'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='，把握自己的人生航向。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='可能是得到上年龄最大的老师。为什么要由我'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='么老龄化是个商业问题？'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='有老龄化、二代传承、内卷等社会性问题。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='，和一个半辈子都看不透的人，人生是天差地'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='地做事，使自己的人生更加精彩。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='人活着都是在追求“三生有幸”，生活、生命'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='大道理，找到自己的航标。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='.16 第三讲'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='.61 第十讲'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='品开始。一路走来，40年一次一次的迭代，'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='的一些趋势，比如碳中和、养老产业的机遇等'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='思考。我常说一句话，人活着都是在追求“三'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='，去做正确的事和正确地做事，使自己的人生'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='起来的人群，他们最难解决的'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='必然，从现象抵达本质。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='1.很多人说南方好'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='.49 第八讲'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='井离乡，将会成为历史。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='碳中和等行业问题，还有老龄化、二代传承、'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='...42 第七讲'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='23 第四讲'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='.......9 第二讲'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='人可能更需要这份报告。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='理背后的大道理，这一条。'), Document(metadata={'source': '20240920_7_王志纲年度报告·2021-页面-1_1.txt'}, page_content='合自己的选择，找到自己的定位。')]\n"
     ]
    }
   ],
   "source": [
    "retriever = db.as_retriever(search_kwargs={'k': 30})  # 构建检索器\n",
    "docs = retriever.get_relevant_documents(query)\n",
    "print(docs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "相似数阈值\n",
    "相似度大于 0.5 的拿出来"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "和、养老产业的机遇等。\n"
     ]
    }
   ],
   "source": [
    "retriever = db.as_retriever(search_type=\"similarity_score_threshold\", search_kwargs={\"score_threshold\": 0.5})  # 构建检索器\n",
    "docs = retriever.get_relevant_documents(query)\n",
    "print(docs[0].page_content)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 模型合成知识图谱元数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T07:19:44.732508Z",
     "start_time": "2024-08-27T07:13:35.394516Z"
    },
    "id": "pXf7OTGHtHNW"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "nodes=[] relationships=[] source=Document(metadata={'source': '1.txt'}, page_content='我和万维钢未曾谋面，但在网上神交已久，互为读者和粉丝。在模糊的记忆中，最早似乎是通过刘夙给的链接发现了同人于野的博客，读过一篇之后便停不下来了。恰逢需要出差，灵机一动，利用一台贪便宜买来又没啥用的MP4的电子书功能，把他博客里的几十篇文章都下载到MP4里面，在动车上大饱眼福。其实旅程的无聊不算什么痛苦，毕竟有窗外的景色可看，知道有好文章却要在数日后方能有时间')\n"
     ]
    }
   ],
   "source": [
    "llm = create_llm()\n",
    "llm_transformer = LLMGraphTransformer(llm=llm)\n",
    "\n",
    "graph_documents = llm_transformer.convert_to_graph_documents(documents)\n",
    "\n",
    "print(graph_documents[0])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "转换数据格式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(6,\n",
       " [GraphDocument(nodes=[], relationships=[], source=Document(metadata={'source': '1.txt', 'id': 'ac1d519a514f00ab646e6a82f59ada86'}, page_content='我和万维钢未曾谋面，但在网上神交已久，互为读者和粉丝。在模糊的记忆中，最早似乎是通过刘夙给的链接发现了同人于野的博客，读过一篇之后便停不下来了。恰逢需要出差，灵机一动，利用一台贪便宜买来又没啥用的MP4的电子书功能，把他博客里的几十篇文章都下载到MP4里面，在动车上大饱眼福。其实旅程的无聊不算什么痛苦，毕竟有窗外的景色可看，知道有好文章却要在数日后方能有时间')),\n",
       "  GraphDocument(nodes=[], relationships=[], source=Document(metadata={'source': '1.txt', 'id': '473e7dc532d1209251bca6a6218258d7'}, page_content='可看，知道有好文章却要在数日后方能有时间看，才足以令人牵肠挂肚，很不舒服。')),\n",
       "  GraphDocument(nodes=[Node(id='长辈', type='Person', properties={}), Node(id='人到老年，看书看皮儿，看报看题儿', type='Statement', properties={}), Node(id='年轻人', type='Person', properties={}), Node(id='看东西多了就会发现，真正有价值，值得花功夫和精力认真去看的作品，少之又少', type='Opinion', properties={}), Node(id='这两个原因固然都有，但不是最主要的。最主要的是，看东西多了就会发现，真正有价值，值得花功夫和精力认真去看的作品，少之又少', type='Opinion', properties={}), Node(id='大概是因为老花眼看不清小字之故，或者是来日无多，学习的性价比下降，学习欲望也就相应减退了', type='Thought', properties={}), Node(id='我', type='Person', properties={})], relationships=[Relationship(source=Node(id='长辈', type='Person', properties={}), target=Node(id='人到老年，看书看皮儿，看报看题儿', type='Statement', properties={}), type='SAYS', properties={}), Relationship(source=Node(id='我', type='Person', properties={}), target=Node(id='大概是因为老花眼看不清小字之故，或者是来日无多，学习的性价比下降，学习欲望也就相应减退了', type='Thought', properties={}), type='ORIGINAL_THOUGHT', properties={}), Relationship(source=Node(id='我', type='Person', properties={}), target=Node(id='这两个原因固然都有，但不是最主要的。最主要的是，看东西多了就会发现，真正有价值，值得花功夫和精力认真去看的作品，少之又少', type='Opinion', properties={}), type='LATER_FOUND', properties={}), Relationship(source=Node(id='年轻人', type='Person', properties={}), target=Node(id='看东西多了就会发现，真正有价值，值得花功夫和精力认真去看的作品，少之又少', type='Opinion', properties={}), type='SHOULD_NOT', properties={})], source=Document(metadata={'source': '1.txt', 'id': '5012e76c2bc2c88c419a6e2fce921705'}, page_content='从前听长辈说，人到老年，看书看皮儿，看报看题儿。我原以为，大概是因为老花眼看不清小字之故。或者是来日无多，学习的性价比下降，学习欲望也就相应减退了。等到自己也到了这个年纪，就发现这两个原因固然都有，但不是最主要的。最主要的是，看东西多了就会发现，真正有价值，值得花功夫和精力认真去看的作品，少之又少。当然，对于年轻人而言，这种态度显然不对，年轻人看什么都新鲜')),\n",
       "  GraphDocument(nodes=[], relationships=[], source=Document(metadata={'source': '1.txt', 'id': '5362bef1e66ca4d9c9ad550323e9cc59'}, page_content='言，这种态度显然不对，年轻人看什么都新鲜，都未知，都长见识，不吃前两个烧饼，只吃第三个烧饼也吃不饱。不阅读很多糟粕，也没有能力发现精华。')),\n",
       "  GraphDocument(nodes=[Node(id='推荐书目', type='Topic', properties={}), Node(id='微博', type='Platform', properties={}), Node(id='所谓名著，就是人人都说应该看，但谁也不看的书', type='Description', properties={}), Node(id='堆在书店里无人问津', type='Status', properties={}), Node(id='I', type='Person', properties={}), Node(id='本年诺贝尔文学奖得主的大作', type='Book', properties={}), Node(id='《万万没想到：用理工科思维理解世界》', type='Book', properties={}), Node(id='名著', type='BookType', properties={})], relationships=[Relationship(source=Node(id='《万万没想到：用理工科思维理解世界》', type='Book', properties={}), target=Node(id='I', type='Person', properties={}), type='RECOMMENDED_BY', properties={}), Relationship(source=Node(id='微博', type='Platform', properties={}), target=Node(id='推荐书目', type='Topic', properties={}), type='MENTIONS', properties={}), Relationship(source=Node(id='名著', type='BookType', properties={}), target=Node(id='所谓名著，就是人人都说应该看，但谁也不看的书', type='Description', properties={}), type='DEFINED_AS', properties={}), Relationship(source=Node(id='本年诺贝尔文学奖得主的大作', type='Book', properties={}), target=Node(id='堆在书店里无人问津', type='Status', properties={}), type='STATUS', properties={})], source=Document(metadata={'source': '1.txt', 'id': '22fa3fb803918b09431cbc05f906754b'}, page_content='微博上常看到推荐书目，也没兴趣点开看都推荐了些什么书，估计不外乎一些名著。提起名著，就想起一句名言：所谓名著，就是人人都说应该看，但谁也不看的书。我也多次看到本年诺贝尔文学奖得主的大作堆在书店里无人问津。如果让我来推荐的话，我不会推荐那些适合用来装X的名著，只会推荐一本书，就是这本尚未成名之著《万万没想到：用理工科思维理解世界》。')),\n",
       "  GraphDocument(nodes=[], relationships=[], source=Document(metadata={'source': '1.txt', 'id': '028447bfd84df33060df34112db35a03'}, page_content='推荐的理由就是：这本书真的有用，而且对大多数人都有用。\\n\\n世上有用的书很多，例如各种专业书籍，你不读就进入不了这个行当，但是如果你并不想进入这个行当，专业书对你的用处就不大。而这本书就如同一部人生指南，只要想改善自己人生的人，这本书都值得一读。'))])"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(graph_documents),graph_documents"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T07:19:49.570849Z",
     "start_time": "2024-08-27T07:19:44.846188Z"
    }
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'graph_documents' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[5], line 27\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;66;03m# from typing import List\u001b[39;00m\n\u001b[0;32m      2\u001b[0m \n\u001b[0;32m      3\u001b[0m \u001b[38;5;66;03m# # 假设 Node 和 Relationship 类已经被定义，并且有如下属性\u001b[39;00m\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     24\u001b[0m \n\u001b[0;32m     25\u001b[0m \u001b[38;5;66;03m# 现在使用清理过的文档来添加图文档\u001b[39;00m\n\u001b[0;32m     26\u001b[0m graph\u001b[38;5;241m.\u001b[39madd_graph_documents(\n\u001b[1;32m---> 27\u001b[0m     graph_documents,\n\u001b[0;32m     28\u001b[0m     baseEntityLabel\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[0;32m     29\u001b[0m     include_source\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m     30\u001b[0m )\n",
      "\u001b[1;31mNameError\u001b[0m: name 'graph_documents' is not defined"
     ]
    }
   ],
   "source": [
    "# from typing import List\n",
    "\n",
    "# # 假设 Node 和 Relationship 类已经被定义，并且有如下属性\n",
    "# # Node: id, type, properties\n",
    "# # Relationship: source, target, type, properties\n",
    "# # GraphDocument: nodes, relationships, source\n",
    "\n",
    "# def clean_node(node):\n",
    "#     # 清理空标签或包含非法字符的标签\n",
    "#     if not node.type or any(ord(c) == 0 for c in node.type):\n",
    "#         # 如果标签为空或包含非法字符，可以设置一个默认标签\n",
    "#         node.type = \"DefaultLabel\"  # 设置一个默认标签\n",
    "#         # 或者可以选择抛出异常或记录日志\n",
    "#         raise ValueError(f\"Invalid label found: {node.type}\")\n",
    "#     return node\n",
    "\n",
    "# def clean_graph_document(graph_doc):\n",
    "#     # 遍历文档中的所有节点并清理标签\n",
    "#     graph_doc.nodes = [clean_node(node) for node in graph_doc.nodes]\n",
    "#     return graph_doc\n",
    "\n",
    "# # 假设 graph_documents 是你的文档列表\n",
    "# cleaned_graph_documents: List[graph_documents] = [clean_graph_document(doc) for doc in graph_documents]\n",
    "\n",
    "# 现在使用清理过的文档来添加图文档\n",
    "graph.add_graph_documents(\n",
    "    graph_documents,\n",
    "    baseEntityLabel=True,\n",
    "    include_source=True\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 制作知识图谱"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T07:32:43.227565Z",
     "start_time": "2024-08-27T07:32:43.154618Z"
    },
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 817,
     "referenced_widgets": [
      "8e37edd9789a4d57a7be401628e7ff7f",
      "9bac7003afd84cecb4e67a81a396ec8d"
     ]
    },
    "id": "RMZlhtDmtHNW",
    "outputId": "86efa842-3297-45d6-dab2-681bbc836b4d"
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "bc761f7fbf1e41178ea54f39ce0c8fd7",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "GraphWidget(layout=Layout(height='500px', width='100%'))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "os.environ[\"NEO4J_URI\"] = \"bolt://localhost:7687\"\n",
    "os.environ[\"NEO4J_USERNAME\"] = \"neo4j\"\n",
    "os.environ[\"NEO4J_PASSWORD\"] = \"12345678\"\n",
    "\n",
    "def showGraph():\n",
    "    driver = GraphDatabase.driver(\n",
    "        uri = os.environ[\"NEO4J_URI\"],\n",
    "        auth = (os.environ[\"NEO4J_USERNAME\"],\n",
    "                os.environ[\"NEO4J_PASSWORD\"]))\n",
    "    session = driver.session()\n",
    "    widget = GraphWidget(graph = session.run(\"MATCH (s)-[r:!MENTIONS]->(t) RETURN s,r,t\").graph())\n",
    "    widget.node_label_mapping = 'id'\n",
    "    return widget\n",
    "\n",
    "showGraph()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 导出为CSV"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 按节点保存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\xyyx\\AppData\\Local\\Temp\\ipykernel_7664\\386438293.py:39: DeprecationWarning: `id` is deprecated, use `element_id` instead\n",
      "  \"id\": record['s'].id,\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "from neo4j import GraphDatabase\n",
    "import pandas as pd\n",
    "\n",
    "os.environ[\"NEO4J_URI\"] = \"bolt://localhost:7687\"\n",
    "os.environ[\"NEO4J_USERNAME\"] = \"neo4j\"\n",
    "os.environ[\"NEO4J_PASSWORD\"] = \"12345678\"\n",
    "def export_to_csv():\n",
    "    driver = GraphDatabase.driver(\n",
    "        uri=os.environ[\"NEO4J_URI\"],\n",
    "        auth=(os.environ[\"NEO4J_USERNAME\"], os.environ[\"NEO4J_PASSWORD\"])\n",
    "    )\n",
    "    \n",
    "    with driver.session() as session:\n",
    "        result = session.run(\"MATCH (s)-[r:!MENTIONS]->(t) RETURN s, r, t\")\n",
    "        \n",
    "        # 提取节点和关系的信息到列表\n",
    "        nodes = []\n",
    "        relationships = []\n",
    "        \n",
    "        for record in result:\n",
    "            # source_node = {\n",
    "            #     \"id\": record['s'].id,\n",
    "            #     # \"labels\": list(record['s'].labels),\n",
    "            #     \"properties\": dict(record['s'])\n",
    "            # }\n",
    "            # target_node = {\n",
    "            #     \"id\": record['t'].id,\n",
    "            #     # \"labels\": list(record['t'].labels),\n",
    "            #     # \"properties\": dict(record['t'])\n",
    "            # }\n",
    "            # relationship = {\n",
    "            #     # \"start_node_id\": record['s'].id,\n",
    "            #     # \"end_node_id\": record['t'].id,\n",
    "            #     \"type\": record['r'].type,\n",
    "            #     # \"properties\": dict(record['r'])\n",
    "            # }\n",
    "            source_node = {\n",
    "                \"id\": record['s'].id,\n",
    "                \"source_properties\": dict(record['s']),\n",
    "                \"type\": record['r'].type,\n",
    "                \"target_properties\": dict(record['t']),\n",
    "                \"relationship_properties\": dict(record['r']),\n",
    "            }\n",
    "\n",
    "\n",
    "            # 确保节点没有重复\n",
    "            # if not any(node['id'] == source_node['id'] for node in nodes):\n",
    "            #     nodes.append(source_node)\n",
    "            nodes.append(source_node)\n",
    "            # if not any(node['id'] == target_node['id'] for node in nodes):\n",
    "            #     nodes.append(target_node)\n",
    "            \n",
    "            # 添加关系\n",
    "            # relationships.append(relationship)\n",
    "        \n",
    "        # 创建DataFrame\n",
    "        nodes_df = pd.DataFrame(nodes)\n",
    "        # relationships_df = pd.DataFrame(relationships)\n",
    "        \n",
    "        # 导出为CSV\n",
    "        nodes_df.to_csv('nodes.csv', index=False)\n",
    "        # relationships_df.to_csv('relationships.csv', index=False)\n",
    "\n",
    "# 调用函数导出数据\n",
    "export_to_csv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 按查询保存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generated Query: 我~2\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "from neo4j import GraphDatabase\n",
    "import pandas as pd\n",
    "\n",
    "# 假设 remove_lucene_chars 和 prompt_query 是已经定义的函数\n",
    "def remove_lucene_chars(input: str) -> str:\n",
    "    # 这里可以实现去除 Lucene 特殊字符的逻辑\n",
    "    return input  # 示例中直接返回输入\n",
    "\n",
    "def prompt_query(question: str) -> list:\n",
    "    # 这里可以实现从问题中提取特征实体的逻辑\n",
    "    return [question]  # 示例中直接返回问题本身作为实体\n",
    "\n",
    "def generate_full_text_query(input: str) -> str:\n",
    "    words = [el for el in remove_lucene_chars(input).split() if el]\n",
    "    if not words:\n",
    "        return \"\"\n",
    "    full_text_query = \" AND \".join([f\"{word}~2\" for word in words])\n",
    "    print(f\"Generated Query: {full_text_query}\")\n",
    "    return full_text_query.strip()\n",
    "\n",
    "def create_fulltext_index():\n",
    "    driver = GraphDatabase.driver(\n",
    "        uri=os.environ[\"NEO4J_URI\"],\n",
    "        auth=(os.environ[\"NEO4J_USERNAME\"], os.environ[\"NEO4J_PASSWORD\"])\n",
    "    )\n",
    "    \n",
    "    with driver.session() as session:\n",
    "        # 创建全文索引\n",
    "        session.run(\n",
    "            \"\"\"\n",
    "            CREATE FULLTEXT INDEX entity IF NOT EXISTS FOR (e:__Entity__) ON EACH [e.id]\n",
    "            \"\"\"\n",
    "        )\n",
    "\n",
    "def export_to_csv(question: str):\n",
    "    driver = GraphDatabase.driver(\n",
    "        uri=os.environ[\"NEO4J_URI\"],\n",
    "        auth=(os.environ[\"NEO4J_USERNAME\"], os.environ[\"NEO4J_PASSWORD\"])\n",
    "    )\n",
    "    \n",
    "    with driver.session() as session:\n",
    "        # 提取特征实体\n",
    "        entities = prompt_query(question)\n",
    "        \n",
    "        data = []\n",
    "        for entity in entities:\n",
    "            query = generate_full_text_query(entity)\n",
    "            if not query:\n",
    "                continue\n",
    "            \n",
    "            result = session.run(\n",
    "                \"\"\"\n",
    "                CALL db.index.fulltext.queryNodes('entity', $query, {limit: 2})\n",
    "                YIELD node, score\n",
    "                CALL {\n",
    "                  WITH node\n",
    "                  MATCH (node)-[r:!MENTIONS]->(neighbor)\n",
    "                  RETURN node.id + ' - ' + type(r) + ' -> ' + neighbor.id AS output\n",
    "                  UNION ALL\n",
    "                  WITH node\n",
    "                  MATCH (node)<-[r:!MENTIONS]-(neighbor)\n",
    "                  RETURN neighbor.id + ' - ' + type(r) + ' -> ' +  node.id AS output\n",
    "                }\n",
    "                RETURN output LIMIT 50\n",
    "                \"\"\",\n",
    "                {\"query\": query}\n",
    "            )\n",
    "            \n",
    "            # 提取查询结果\n",
    "            data.extend([record['output'] for record in result])\n",
    "        \n",
    "        # 创建DataFrame\n",
    "        df = pd.DataFrame(data, columns=['output'])\n",
    "        \n",
    "        # 导出为CSV\n",
    "        df.to_csv('output.csv', index=False)\n",
    "\n",
    "# 调用函数创建全文索引\n",
    "create_fulltext_index()\n",
    "\n",
    "# 调用函数导出数据\n",
    "export_to_csv(\"我\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 按知识图谱保存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from neo4j import GraphDatabase\n",
    "import pandas as pd\n",
    "\n",
    "def create_fulltext_index():\n",
    "    driver = GraphDatabase.driver(\n",
    "        uri=os.environ[\"NEO4J_URI\"],\n",
    "        auth=(os.environ[\"NEO4J_USERNAME\"], os.environ[\"NEO4J_PASSWORD\"])\n",
    "    )\n",
    "    \n",
    "    with driver.session() as session:\n",
    "        # 创建全文索引\n",
    "        session.run(\n",
    "            \"\"\"\n",
    "            CREATE FULLTEXT INDEX entity IF NOT EXISTS FOR (e:__Entity__) ON EACH [e.id]\n",
    "            \"\"\"\n",
    "        )\n",
    "\n",
    "def export_all_relationships_to_csv():\n",
    "    driver = GraphDatabase.driver(\n",
    "        uri=os.environ[\"NEO4J_URI\"],\n",
    "        auth=(os.environ[\"NEO4J_USERNAME\"], os.environ[\"NEO4J_PASSWORD\"])\n",
    "    )\n",
    "    \n",
    "    with driver.session() as session:\n",
    "        # 查询所有关系数据\n",
    "        result = session.run(\n",
    "            \"\"\"\n",
    "            MATCH (s)-[r:!MENTIONS]->(t)\n",
    "            RETURN s.id + ' - ' + type(r) + ' -> ' + t.id AS output\n",
    "            UNION ALL\n",
    "            MATCH (s)<-[r:!MENTIONS]-(t)\n",
    "            RETURN t.id + ' - ' + type(r) + ' -> ' + s.id AS output\n",
    "            \"\"\"\n",
    "        )\n",
    "        \n",
    "        # 提取查询结果\n",
    "        data = [record['output'] for record in result]\n",
    "        \n",
    "        # 创建DataFrame\n",
    "        df = pd.DataFrame(data, columns=['output'])\n",
    "        \n",
    "        # 导出为CSV\n",
    "        df.to_csv('graph_relationship_data.csv', index=False)\n",
    "\n",
    "# 调用函数创建全文索引\n",
    "create_fulltext_index()\n",
    "\n",
    "# 调用函数导出所有关系数据\n",
    "export_all_relationships_to_csv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 按csv格式保存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from neo4j import GraphDatabase\n",
    "import pandas as pd \n",
    "\n",
    "def create_fulltext_index():\n",
    "    driver = GraphDatabase.driver(\n",
    "        uri=os.environ[\"NEO4J_URI\"],\n",
    "        auth=(os.environ[\"NEO4J_USERNAME\"], os.environ[\"NEO4J_PASSWORD\"])\n",
    "    )\n",
    "    \n",
    "    with driver.session() as session:\n",
    "        # 创建全文索引\n",
    "        session.run(\n",
    "            \"\"\"\n",
    "            CREATE FULLTEXT INDEX entity IF NOT EXISTS FOR (e:__Entity__) ON EACH [e.id]\n",
    "            \"\"\"\n",
    "        )\n",
    "\n",
    "def export_all_relationships_to_csv():\n",
    "    driver = GraphDatabase.driver(\n",
    "        uri=os.environ[\"NEO4J_URI\"],\n",
    "        auth=(os.environ[\"NEO4J_USERNAME\"], os.environ[\"NEO4J_PASSWORD\"])\n",
    "    )\n",
    "    \n",
    "    with driver.session() as session:\n",
    "        # 查询所有关系数据（排除 MENTIONS 关系）\n",
    "        result = session.run(\n",
    "            \"\"\"\n",
    "            MATCH (n)-[r]->(neighbor)\n",
    "            WHERE NOT type(r) = 'MENTIONS'\n",
    "            RETURN n.id AS source, type(r) AS relationship, neighbor.id AS target\n",
    "            UNION\n",
    "            MATCH (n)<-[r]-(neighbor)\n",
    "            WHERE NOT type(r) = 'MENTIONS'\n",
    "            RETURN neighbor.id AS source, type(r) AS relationship, n.id AS target\n",
    "            \"\"\"\n",
    "        )\n",
    "        \n",
    "        # 提取查询结果\n",
    "        data = [(record['source'], record['relationship'], record['target']) for record in result]\n",
    "        \n",
    "        # 创建DataFrame\n",
    "        df = pd.DataFrame(data, columns=['source_id', 'relationship_type', 'target_id'])\n",
    "        \n",
    "        # 导出为CSV\n",
    "        df.to_csv('all_relationships.csv', index=False)\n",
    "\n",
    "# 调用函数创建全文索引\n",
    "# create_fulltext_index()\n",
    "\n",
    "# 调用函数导出所有关系数据\n",
    "export_all_relationships_to_csv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 加载CSV"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import csv\n",
    "from neo4j import GraphDatabase\n",
    "import os\n",
    "\n",
    "os.environ[\"NEO4J_URI\"] = \"bolt://localhost:7687\"\n",
    "os.environ[\"NEO4J_USERNAME\"] = \"neo4j\"\n",
    "os.environ[\"NEO4J_PASSWORD\"] = \"12345678\"\n",
    "# 连接到 Neo4j 数据库\n",
    "driver = GraphDatabase.driver(\n",
    "    uri=os.environ[\"NEO4J_URI\"],\n",
    "    auth=(os.environ[\"NEO4J_USERNAME\"], os.environ[\"NEO4J_PASSWORD\"])\n",
    ")\n",
    "\n",
    "def load_nodes(session, nodes_file):\n",
    "    with open(nodes_file, mode='r', encoding='utf-8') as file:\n",
    "        reader = csv.DictReader(file)\n",
    "        for row in reader:\n",
    "            # 假设每个节点都有一个唯一的 ID 和标签\n",
    "            node_id = row['id']\n",
    "            labels = row['labels'].split(':')  # 如果有多个标签，用:分隔\n",
    "            properties = {k: v for k, v in row.items() if k not in ['id', 'labels']}\n",
    "            \n",
    "            # 创建节点\n",
    "            session.run(\n",
    "                f\"MERGE (n:{':'.join(labels)} {{id: $node_id}}) ON CREATE SET n += $properties\",\n",
    "                node_id=node_id,\n",
    "                properties=properties\n",
    "            )\n",
    "\n",
    "def load_relationships(session, relationships_file):\n",
    "    with open(relationships_file, mode='r', encoding='utf-8') as file:\n",
    "        reader = csv.DictReader(file)\n",
    "        for row in reader:\n",
    "            # 假设每条关系都有起始节点ID、结束节点ID、类型和属性\n",
    "            start_node_id = row['start_node']\n",
    "            end_node_id = row['end_node']\n",
    "            rel_type = row['type']\n",
    "            properties = {k: v for k, v in row.items() if k not in ['start_node', 'end_node', 'type']}\n",
    "            \n",
    "            # 创建关系\n",
    "            session.run(\n",
    "                \"MATCH (a {id: $start_node_id}), (b {id: $end_node_id}) \"\n",
    "                \"MERGE (a)-[r:%s]->(b) ON CREATE SET r += $properties\" % rel_type,\n",
    "                start_node_id=start_node_id,\n",
    "                end_node_id=end_node_id,\n",
    "                properties=properties\n",
    "            )\n",
    "\n",
    "with driver.session() as session:\n",
    "    # 加载节点\n",
    "    load_nodes(session, 'nodes.csv')\n",
    "    # 加载关系\n",
    "    load_relationships(session, 'relationships.csv')\n",
    "\n",
    "# 关闭连接\n",
    "driver.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 导出json"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\xyyx\\AppData\\Local\\Temp\\ipykernel_3124\\789227155.py:14: DeprecationWarning: `id` is deprecated, use `element_id` instead\n",
      "  \"source\": {\"id\": record['s'].id, \"labels\": list(record['s'].labels), \"properties\": dict(record['s'])},\n",
      "C:\\Users\\xyyx\\AppData\\Local\\Temp\\ipykernel_3124\\789227155.py:16: DeprecationWarning: `id` is deprecated, use `element_id` instead\n",
      "  \"target\": {\"id\": record['t'].id, \"labels\": list(record['t'].labels), \"properties\": dict(record['t'])}\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "\n",
    "def export_to_json():\n",
    "    driver = GraphDatabase.driver(\n",
    "        uri=os.environ[\"NEO4J_URI\"],\n",
    "        auth=(os.environ[\"NEO4J_USERNAME\"], os.environ[\"NEO4J_PASSWORD\"])\n",
    "    )\n",
    "    with driver.session() as session:\n",
    "        result = session.run(\"MATCH (s)-[r:!MENTIONS]->(t) RETURN s, r, t\")\n",
    "        # 将结果转换为字典列表\n",
    "        data = []\n",
    "        for record in result:\n",
    "            data.append({\n",
    "                \"source\": {\"id\": record['s'].id, \"labels\": list(record['s'].labels), \"properties\": dict(record['s'])},\n",
    "                \"relationship\": {\"type\": record['r'].type, \"properties\": dict(record['r'])},\n",
    "                \"target\": {\"id\": record['t'].id, \"labels\": list(record['t'].labels), \"properties\": dict(record['t'])}\n",
    "            })\n",
    "        # 写入JSON文件\n",
    "        with open('graph_data.json', 'w', encoding='utf-8') as f:\n",
    "            json.dump(data, f, indent=2,ensure_ascii=False)\n",
    "\n",
    "export_to_json()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\xyyx\\AppData\\Local\\Temp\\ipykernel_3124\\3671501715.py:23: DeprecationWarning: `id` is deprecated, use `element_id` instead\n",
      "  source_id = record['s'].id\n",
      "C:\\Users\\xyyx\\AppData\\Local\\Temp\\ipykernel_3124\\3671501715.py:28: DeprecationWarning: `id` is deprecated, use `element_id` instead\n",
      "  target_id = record['t'].id\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "from collections import defaultdict\n",
    "from neo4j import GraphDatabase\n",
    "\n",
    "def export_to_json():\n",
    "    driver = GraphDatabase.driver(\n",
    "        uri=os.environ[\"NEO4J_URI\"],\n",
    "        auth=(os.environ[\"NEO4J_USERNAME\"], os.environ[\"NEO4J_PASSWORD\"])\n",
    "    )\n",
    "    \n",
    "    with driver.session() as session:\n",
    "        # 查询所有节点和关系\n",
    "        result = session.run(\"MATCH (s)-[r:!MENTIONS]->(t) RETURN s, r, t\")\n",
    "        \n",
    "        # 用于存储节点信息\n",
    "        nodes = {}\n",
    "        # 用于存储关系信息\n",
    "        relationships = []\n",
    "        \n",
    "        # 遍历结果\n",
    "        for record in result:\n",
    "            # 获取源节点信息\n",
    "            source_id = record['s'].id\n",
    "            source_labels = list(record['s'].labels)\n",
    "            source_properties = dict(record['s'])\n",
    "            \n",
    "            # 获取目标节点信息\n",
    "            target_id = record['t'].id\n",
    "            target_labels = list(record['t'].labels)\n",
    "            target_properties = dict(record['t'])\n",
    "            \n",
    "            # 获取关系信息\n",
    "            relationship_type = record['r'].type\n",
    "            relationship_properties = dict(record['r'])\n",
    "            \n",
    "            # 确保源节点和目标节点没有重复\n",
    "            if source_id not in nodes:\n",
    "                nodes[source_id] = {\n",
    "                    \"id\": source_id,\n",
    "                    \"labels\": source_labels,\n",
    "                    \"properties\": source_properties\n",
    "                }\n",
    "            if target_id not in nodes:\n",
    "                nodes[target_id] = {\n",
    "                    \"id\": target_id,\n",
    "                    \"labels\": target_labels,\n",
    "                    \"properties\": target_properties\n",
    "                }\n",
    "            \n",
    "            # 添加关系\n",
    "            relationships.append({\n",
    "                \"source_id\": source_id,\n",
    "                \"target_id\": target_id,\n",
    "                \"relationship\": {\n",
    "                    \"type\": relationship_type,\n",
    "                    \"properties\": relationship_properties\n",
    "                }\n",
    "            })\n",
    "        \n",
    "        # 将节点和关系转换为最终的导出格式\n",
    "        data = []\n",
    "        for rel in relationships:\n",
    "            data.append({\n",
    "                \"source\": nodes[rel['source_id']],\n",
    "                \"relationship\": rel['relationship'],\n",
    "                \"target\": nodes[rel['target_id']]\n",
    "            })\n",
    "        \n",
    "        # 写入JSON文件\n",
    "        with open('graph_data.json', 'w', encoding='utf-8') as f:\n",
    "            json.dump(data, f, indent=2, ensure_ascii=False)\n",
    "\n",
    "# 调用函数导出数据\n",
    "export_to_json()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 加载json数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "ename": "ConstraintError",
     "evalue": "{code: Neo.ClientError.Schema.ConstraintValidationFailed} {message: Node(8) already exists with label `__Entity__` and property `id` = '我'}",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mConstraintError\u001b[0m                           Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[13], line 52\u001b[0m\n\u001b[0;32m     39\u001b[0m             session\u001b[38;5;241m.\u001b[39mrun(\n\u001b[0;32m     40\u001b[0m \u001b[38;5;250m                \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m     41\u001b[0m \u001b[38;5;124;03m                MATCH (s {id: $source_id}), (t {id: $target_id})\u001b[39;00m\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     48\u001b[0m                 properties\u001b[38;5;241m=\u001b[39mrelationship_properties\n\u001b[0;32m     49\u001b[0m             )\n\u001b[0;32m     51\u001b[0m \u001b[38;5;66;03m# 调用函数加载数据\u001b[39;00m\n\u001b[1;32m---> 52\u001b[0m \u001b[43mload_data_from_json\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mgraph_data.json\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "Cell \u001b[1;32mIn[13], line 20\u001b[0m, in \u001b[0;36mload_data_from_json\u001b[1;34m(json_file)\u001b[0m\n\u001b[0;32m     18\u001b[0m source_labels \u001b[38;5;241m=\u001b[39m item[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124msource\u001b[39m\u001b[38;5;124m'\u001b[39m][\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlabels\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[0;32m     19\u001b[0m source_properties \u001b[38;5;241m=\u001b[39m item[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124msource\u001b[39m\u001b[38;5;124m'\u001b[39m][\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mproperties\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[1;32m---> 20\u001b[0m \u001b[43msession\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m     21\u001b[0m \u001b[43m    \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mMERGE (s:\u001b[39;49m\u001b[38;5;132;43;01m%s\u001b[39;49;00m\u001b[38;5;124;43m \u001b[39;49m\u001b[38;5;124;43m{\u001b[39;49m\u001b[38;5;124;43mid: $id}) ON CREATE SET s += $properties ON MATCH SET s += $properties\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m%\u001b[39;49m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m:\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m(\u001b[49m\u001b[43msource_labels\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m     22\u001b[0m \u001b[43m    \u001b[49m\u001b[38;5;28;43mid\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43msource_id\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m     23\u001b[0m \u001b[43m    \u001b[49m\u001b[43mproperties\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43msource_properties\u001b[49m\n\u001b[0;32m     24\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m     26\u001b[0m \u001b[38;5;66;03m# 创建或匹配目标节点\u001b[39;00m\n\u001b[0;32m     27\u001b[0m target_id \u001b[38;5;241m=\u001b[39m item[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtarget\u001b[39m\u001b[38;5;124m'\u001b[39m][\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mid\u001b[39m\u001b[38;5;124m'\u001b[39m]\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\neo4j\\_sync\\work\\session.py:314\u001b[0m, in \u001b[0;36mSession.run\u001b[1;34m(self, query, parameters, **kwargs)\u001b[0m\n\u001b[0;32m    312\u001b[0m bookmarks \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_bookmarks()\n\u001b[0;32m    313\u001b[0m parameters \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mdict\u001b[39m(parameters \u001b[38;5;129;01mor\u001b[39;00m {}, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m--> 314\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_auto_result\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_run\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    315\u001b[0m \u001b[43m    \u001b[49m\u001b[43mquery\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparameters\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_config\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdatabase\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    316\u001b[0m \u001b[43m    \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_config\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mimpersonated_user\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_config\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdefault_access_mode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    317\u001b[0m \u001b[43m    \u001b[49m\u001b[43mbookmarks\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_config\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mnotifications_min_severity\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    318\u001b[0m \u001b[43m    \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_config\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mnotifications_disabled_classifications\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    319\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    321\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_auto_result\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\neo4j\\_sync\\work\\result.py:221\u001b[0m, in \u001b[0;36mResult._run\u001b[1;34m(self, query, parameters, db, imp_user, access_mode, bookmarks, notifications_min_severity, notifications_disabled_classifications)\u001b[0m\n\u001b[0;32m    219\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_pull()\n\u001b[0;32m    220\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_connection\u001b[38;5;241m.\u001b[39msend_all()\n\u001b[1;32m--> 221\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_attach\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\neo4j\\_sync\\work\\result.py:409\u001b[0m, in \u001b[0;36mResult._attach\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    407\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_exhausted \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m:\n\u001b[0;32m    408\u001b[0m     \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_attached \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m:\n\u001b[1;32m--> 409\u001b[0m         \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_connection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfetch_message\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\neo4j\\_sync\\io\\_common.py:178\u001b[0m, in \u001b[0;36mConnectionErrorHandler.__getattr__.<locals>.outer.<locals>.inner\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m    176\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21minner\u001b[39m(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m    177\u001b[0m     \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 178\u001b[0m         \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    179\u001b[0m     \u001b[38;5;28;01mexcept\u001b[39;00m (Neo4jError, ServiceUnavailable, SessionExpired) \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[0;32m    180\u001b[0m         \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m asyncio\u001b[38;5;241m.\u001b[39miscoroutinefunction(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m__on_error)\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\neo4j\\_sync\\io\\_bolt.py:860\u001b[0m, in \u001b[0;36mBolt.fetch_message\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    856\u001b[0m \u001b[38;5;66;03m# Receive exactly one message\u001b[39;00m\n\u001b[0;32m    857\u001b[0m tag, fields \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39minbox\u001b[38;5;241m.\u001b[39mpop(\n\u001b[0;32m    858\u001b[0m     hydration_hooks\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mresponses[\u001b[38;5;241m0\u001b[39m]\u001b[38;5;241m.\u001b[39mhydration_hooks\n\u001b[0;32m    859\u001b[0m )\n\u001b[1;32m--> 860\u001b[0m res \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_process_message\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtag\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfields\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    861\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39midle_since \u001b[38;5;241m=\u001b[39m monotonic()\n\u001b[0;32m    862\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m res\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\neo4j\\_sync\\io\\_bolt5.py:370\u001b[0m, in \u001b[0;36mBolt5x0._process_message\u001b[1;34m(self, tag, fields)\u001b[0m\n\u001b[0;32m    368\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_server_state_manager\u001b[38;5;241m.\u001b[39mstate \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbolt_states\u001b[38;5;241m.\u001b[39mFAILED\n\u001b[0;32m    369\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 370\u001b[0m     \u001b[43mresponse\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mon_failure\u001b[49m\u001b[43m(\u001b[49m\u001b[43msummary_metadata\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    371\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m (ServiceUnavailable, DatabaseUnavailable):\n\u001b[0;32m    372\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpool:\n",
      "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python312\\site-packages\\neo4j\\_sync\\io\\_common.py:245\u001b[0m, in \u001b[0;36mResponse.on_failure\u001b[1;34m(self, metadata)\u001b[0m\n\u001b[0;32m    243\u001b[0m handler \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhandlers\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mon_summary\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m    244\u001b[0m Util\u001b[38;5;241m.\u001b[39mcallback(handler)\n\u001b[1;32m--> 245\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m Neo4jError\u001b[38;5;241m.\u001b[39mhydrate(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mmetadata)\n",
      "\u001b[1;31mConstraintError\u001b[0m: {code: Neo.ClientError.Schema.ConstraintValidationFailed} {message: Node(8) already exists with label `__Entity__` and property `id` = '我'}"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import json\n",
    "from neo4j import GraphDatabase\n",
    "\n",
    "def load_data_from_json(json_file):\n",
    "    driver = GraphDatabase.driver(\n",
    "        uri=os.environ[\"NEO4J_URI\"],\n",
    "        auth=(os.environ[\"NEO4J_USERNAME\"], os.environ[\"NEO4J_PASSWORD\"])\n",
    "    )\n",
    "    \n",
    "    with open(json_file, 'r', encoding='utf-8') as f:\n",
    "        data = json.load(f)\n",
    "    \n",
    "    with driver.session() as session:\n",
    "        for item in data:\n",
    "            # 创建或匹配源节点\n",
    "            source_id = item['source']['id']\n",
    "            source_labels = item['source']['labels']\n",
    "            source_properties = item['source']['properties']\n",
    "            session.run(\n",
    "                \"MERGE (s:%s {id: $id}) ON CREATE SET s += $properties ON MATCH SET s += $properties\" % (\":\".join(source_labels)),\n",
    "                id=source_id,\n",
    "                properties=source_properties\n",
    "            )\n",
    "            \n",
    "            # 创建或匹配目标节点\n",
    "            target_id = item['target']['id']\n",
    "            target_labels = item['target']['labels']\n",
    "            target_properties = item['target']['properties']\n",
    "            session.run(\n",
    "                \"MERGE (t:%s {id: $id}) ON CREATE SET t += $properties ON MATCH SET t += $properties\" % (\":\".join(target_labels)),\n",
    "                id=target_id,\n",
    "                properties=target_properties\n",
    "            )\n",
    "            \n",
    "            # 创建或匹配关系\n",
    "            relationship_type = item['relationship']['type']\n",
    "            relationship_properties = item['relationship']['properties']\n",
    "            session.run(\n",
    "                \"\"\"\n",
    "                MATCH (s {id: $source_id}), (t {id: $target_id})\n",
    "                MERGE (s)-[r:%s]->(t)\n",
    "                ON CREATE SET r += $properties\n",
    "                ON MATCH SET r += $properties\n",
    "                \"\"\" % relationship_type,\n",
    "                source_id=source_id,\n",
    "                target_id=target_id,\n",
    "                properties=relationship_properties\n",
    "            )\n",
    "\n",
    "# 调用函数加载数据\n",
    "load_data_from_json('graph_data.json')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 向量化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T07:20:01.075446Z",
     "start_time": "2024-08-27T07:19:59.732108Z"
    },
    "id": "GHbJPMfDtHNW"
   },
   "outputs": [],
   "source": [
    "vector_index = Neo4jVector.from_existing_graph(\n",
    "    create_embeddings(),\n",
    "    search_type=\"hybrid\",\n",
    "    node_label=\"Document\",\n",
    "    text_node_properties=[\"text\"],\n",
    "    embedding_node_property=\"embedding\"\n",
    ")\n",
    "vector_retriever = vector_index.as_retriever()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 提取问题的特征关键词"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T07:20:38.103559Z",
     "start_time": "2024-08-27T07:20:38.095701Z"
    },
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "6yCMz_sRtHNW",
    "outputId": "f533f279-9a2b-48d6-830b-28d04c43550b"
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[WARNING][2024-10-05 17:03:44.456] redis_rate_limiter.py:21 [t:12340]: no redis installed, RedisRateLimiter unavailable\n",
      "C:\\Users\\XY-33\\AppData\\Local\\Temp\\ipykernel_3172\\2549023785.py:16: LangChainDeprecationWarning: The method `BaseLLM.__call__` was deprecated in langchain-core 0.1.7 and will be removed in 1.0. Use :meth:`~invoke` instead.\n",
      "  response = llm(prompt)\n",
      "[INFO][2024-10-05 17:03:45.459] oauth.py:277 [t:12340]: trying to refresh token for ak `evU2Fd***`\n",
      "[INFO][2024-10-05 17:03:45.548] oauth.py:304 [t:12340]: successfully refresh token\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'麻辣烫，配送速度，包装'"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def prompt_query(query):\n",
    "    llm = create_llm()\n",
    "    prompt = (\n",
    "        \"任务：query特征提取,只要输出词语，不要输出任何其他信息。\\n\"\n",
    "        \"例子：\\n\"\n",
    "        \"输入：小红和小丽都是谁呀？\\n\"\n",
    "        \"输出：小红，小丽\\n\"\n",
    "        \"输入：我不管我要吃巧克力和香蕉\\n\"\n",
    "        \"输出：巧克力，香蕉\\n\"\n",
    "        \"输入：巴黎奥运金牌第一名\\n\"\n",
    "        \"输出：巴黎奥运会，金牌\\n\"\n",
    "        \"按照上述事实回答问题:\\n\"\n",
    "        f\"输入：{query}\\n\"\n",
    "        \"输出：\"\n",
    "    )\n",
    "    response = llm(prompt)\n",
    "    # response_list = [response.strip()] if response else []\n",
    "    return ','.join(response) if isinstance(response, list) else response.strip()\n",
    "prompt_query(\"麻辣烫的配送速度与包装做得好吗？\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T04:36:08.764058Z",
     "start_time": "2024-08-27T04:36:08.082651Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "躺平，啃老~2\n"
     ]
    }
   ],
   "source": [
    "def generate_full_text_query(input: str) -> str:\n",
    "    full_text_query = \"\"\n",
    "    words = [el for el in remove_lucene_chars(input).split() if el]\n",
    "    for word in words[:-1]:\n",
    "        full_text_query += f\" {word}~2 AND\"\n",
    "    full_text_query += f\" {words[-1]}~2\"\n",
    "    return full_text_query.strip()\n",
    "\n",
    "entities = prompt_query(\"就是大批的青年人“躺平”或者“啃老”\")\n",
    "print(generate_full_text_query(entities))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 用知识图谱检索领近的节点"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T07:20:44.273159Z",
     "start_time": "2024-08-27T07:20:44.230767Z"
    },
    "id": "dY8huoM8tHNX"
   },
   "outputs": [],
   "source": [
    "def generate_full_text_query(input: str) -> str:\n",
    "    words = [el for el in remove_lucene_chars(input).split() if el]\n",
    "    if not words:\n",
    "        return \"\"\n",
    "    full_text_query = \" AND \".join([f\"{word}~2\" for word in words])\n",
    "    print(f\"Generated Query: {full_text_query}\")\n",
    "    return full_text_query.strip()\n",
    "\n",
    "graph.query(\n",
    "    \"CREATE FULLTEXT INDEX entity IF NOT EXISTS FOR (e:__Entity__) ON EACH [e.id]\")\n",
    "\n",
    "\n",
    "def graph_retriever(question: str) -> str:\n",
    "    \"\"\"\n",
    "    Collects the neighborhood of entities mentioned\n",
    "    in the question\n",
    "    \"\"\"\n",
    "    result = \"\"\n",
    "    #prompt_query提取特征实体\n",
    "    entities = prompt_query(question)\n",
    "    for entity in entities:\n",
    "        response = graph.query(\n",
    "            \"\"\"CALL db.index.fulltext.queryNodes('entity', $query, {limit:2})\n",
    "            YIELD node,score\n",
    "            CALL {\n",
    "              WITH node\n",
    "              MATCH (node)-[r:!MENTIONS]->(neighbor)\n",
    "              RETURN node.id + ' - ' + type(r) + ' -> ' + neighbor.id AS output\n",
    "              UNION ALL\n",
    "              WITH node\n",
    "              MATCH (node)<-[r:!MENTIONS]-(neighbor)\n",
    "              RETURN neighbor.id + ' - ' + type(r) + ' -> ' +  node.id AS output\n",
    "            }\n",
    "            RETURN output LIMIT 50\n",
    "            \"\"\",\n",
    "            {\"query\": generate_full_text_query(entity)},\n",
    "        )\n",
    "        result += \"\\n\".join([el['output'] for el in response])\n",
    "    return result\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-28T10:09:10.791740Z",
     "start_time": "2024-08-28T10:09:08.069499Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Generated Query: 年~2\n",
      "Generated Query: 轻~2\n",
      "Generated Query: 人~2\n",
      "老年人 - IS_NOT -> 一穷二白，守着两亩薄田过活的人\n",
      "大批的青年人 - BEHAVIOR -> 躺平\n",
      "大批的青年人 - BEHAVIOR -> 啃老养老观念 - BELONGS_TO -> 中国人\n",
      "老龄化 - ASSOCIATED_WITH -> 个人发展机会减少\n",
      "老龄化 - ASSOCIATED_WITH -> 家庭负担增加\n",
      "老龄化 - ASSOCIATED_WITH -> 经济减速富人 - HAS_ASSET -> 1000万以上\n",
      "老年人 - IS_NOT -> 一穷二白，守着两亩薄田过活的人\n"
     ]
    }
   ],
   "source": [
    "print(graph_retriever(\"年轻人\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T07:20:57.712856Z",
     "start_time": "2024-08-27T07:20:57.704088Z"
    },
    "id": "iCTMp3prtHNX"
   },
   "outputs": [],
   "source": [
    "def full_retriever(question: str):\n",
    "    graph_data = graph_retriever(question)\n",
    "    vector_data = [el.page_content for el in vector_retriever.invoke(question)]\n",
    "    final_data = f\"\"\"Graph data:\n",
    "{graph_data}\n",
    "vector data:\n",
    "{\"#Document \". join(vector_data)}\n",
    "    \"\"\"\n",
    "    print(final_data)\n",
    "    return final_data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## RAG实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T07:21:00.036382Z",
     "start_time": "2024-08-27T07:21:00.020986Z"
    },
    "id": "Dzb2jcittHNY"
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'llm' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[25], line 12\u001b[0m\n\u001b[0;32m      1\u001b[0m template \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\"\"\u001b[39m\n\u001b[0;32m      2\u001b[0m \u001b[38;5;124m仅根据下列上下文回答问题:\u001b[39m\n\u001b[0;32m      3\u001b[0m \u001b[38;5;132;01m{context}\u001b[39;00m\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m      6\u001b[0m \u001b[38;5;124m使用自然语言，简洁明了.\u001b[39m\n\u001b[0;32m      7\u001b[0m \u001b[38;5;124m\"\"\"\u001b[39m\n\u001b[0;32m      8\u001b[0m prompt \u001b[38;5;241m=\u001b[39m ChatPromptTemplate\u001b[38;5;241m.\u001b[39mfrom_template(template)\n\u001b[0;32m     10\u001b[0m chain \u001b[38;5;241m=\u001b[39m ({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontext\u001b[39m\u001b[38;5;124m\"\u001b[39m: full_retriever,\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mquestion\u001b[39m\u001b[38;5;124m\"\u001b[39m: RunnablePassthrough(),}\n\u001b[0;32m     11\u001b[0m     \u001b[38;5;241m|\u001b[39m prompt\n\u001b[1;32m---> 12\u001b[0m     \u001b[38;5;241m|\u001b[39m llm\n\u001b[0;32m     13\u001b[0m     \u001b[38;5;241m|\u001b[39m StrOutputParser()\n\u001b[0;32m     14\u001b[0m )\n",
      "\u001b[1;31mNameError\u001b[0m: name 'llm' is not defined"
     ]
    }
   ],
   "source": [
    "template = \"\"\"\n",
    "仅根据下列上下文回答问题:\n",
    "{context}\n",
    "\n",
    "Question: {question}\n",
    "使用自然语言，简洁明了.\n",
    "\"\"\"\n",
    "prompt = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "chain = ({\"context\": full_retriever,\"question\": RunnablePassthrough(),}\n",
    "    | prompt\n",
    "    | llm\n",
    "    | StrOutputParser()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-08-27T07:21:06.294520Z",
     "start_time": "2024-08-27T07:21:02.431231Z"
    },
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 53
    },
    "id": "dtU0iMNgtHNY",
    "outputId": "bf3cf94c-f030-41b4-fb3b-356c5bff98f5"
   },
   "outputs": [],
   "source": [
    "chain.invoke(input=\"麻辣烫的汤怎么样?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\xyyx\\AppData\\Local\\Temp\\ipykernel_12876\\3478570617.py:13: DeprecationWarning: write_transaction has been renamed to execute_write\n",
      "  session.write_transaction(clear_database)\n"
     ]
    }
   ],
   "source": [
    "os.environ[\"NEO4J_URI\"] = \"bolt://localhost:7687\"\n",
    "os.environ[\"NEO4J_USERNAME\"] = \"neo4j\"\n",
    "os.environ[\"NEO4J_PASSWORD\"] = \"12345678\"\n",
    "driver = GraphDatabase.driver(uri = os.environ[\"NEO4J_URI\"],auth = (os.environ[\"NEO4J_USERNAME\"],os.environ[\"NEO4J_PASSWORD\"]))\n",
    "\n",
    "def clear_database(tx):\n",
    "    # 删除所有关系\n",
    "    tx.run(\"MATCH ()-[r]->() DELETE r\")\n",
    "    # 删除所有节点\n",
    "    tx.run(\"MATCH (n) DELETE n\")\n",
    "\n",
    "with driver.session() as session:\n",
    "    session.write_transaction(clear_database)\n",
    "\n",
    "# 关闭驱动\n",
    "driver.close()"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "include_colab_link": true,
   "provenance": []
  },
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.4"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "8e37edd9789a4d57a7be401628e7ff7f": {
     "model_module": "yfiles-jupyter-graphs",
     "model_module_version": "^1.6.1",
     "model_name": "GraphModel",
     "state": {
      "_context_pane_mapping": [
       {
        "id": "Neighborhood",
        "title": "Neighborhood"
       },
       {
        "id": "Data",
        "title": "Data"
       },
       {
        "id": "Search",
        "title": "Search"
       },
       {
        "id": "About",
        "title": "About"
       }
      ],
      "_data_importer": "neo4j",
      "_directed": true,
      "_dom_classes": [],
      "_edges": [
       {
        "color": "#9C27B0",
        "directed": true,
        "end": 2,
        "id": 14,
        "label": "RULED",
        "properties": {
         "label": "RULED"
        },
        "start": 1,
        "thickness_factor": 1
       },
       {
        "color": "#9C27B0",
        "directed": true,
        "end": 3,
        "id": 15,
        "label": "RULED",
        "properties": {
         "label": "RULED"
        },
        "start": 1,
        "thickness_factor": 1
       },
       {
        "color": "#2196F3",
        "directed": true,
        "end": 4,
        "id": 16,
        "label": "BELONGED_TO",
        "properties": {
         "label": "BELONGED_TO"
        },
        "start": 1,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 5,
        "id": 17,
        "label": "PARENT",
        "properties": {
         "label": "PARENT"
        },
        "start": 1,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 6,
        "id": 18,
        "label": "PARENT",
        "properties": {
         "label": "PARENT"
        },
        "start": 1,
        "thickness_factor": 1
       },
       {
        "color": "#F44336",
        "directed": true,
        "end": 6,
        "id": 19,
        "label": "SPOUSE",
        "properties": {
         "label": "SPOUSE"
        },
        "start": 5,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 7,
        "id": 20,
        "label": "PARENT",
        "properties": {
         "label": "PARENT"
        },
        "start": 5,
        "thickness_factor": 1
       },
       {
        "color": "#607D8B",
        "directed": true,
        "end": 8,
        "id": 21,
        "label": "BEQUEATHED_CROWN_TO",
        "properties": {
         "label": "BEQUEATHED_CROWN_TO"
        },
        "start": 7,
        "thickness_factor": 1
       },
       {
        "color": "#673AB7",
        "directed": true,
        "end": 9,
        "id": 22,
        "label": "IGNORED_CLAIMS_OF",
        "properties": {
         "label": "IGNORED_CLAIMS_OF"
        },
        "start": 7,
        "thickness_factor": 1
       },
       {
        "color": "#673AB7",
        "directed": true,
        "end": 1,
        "id": 23,
        "label": "IGNORED_CLAIMS_OF",
        "properties": {
         "label": "IGNORED_CLAIMS_OF"
        },
        "start": 7,
        "thickness_factor": 1
       },
       {
        "color": "#CDDC39",
        "directed": true,
        "end": 1,
        "id": 24,
        "label": "IMPRISONED",
        "properties": {
         "label": "IMPRISONED"
        },
        "start": 9,
        "thickness_factor": 1
       },
       {
        "color": "#9E9E9E",
        "directed": true,
        "end": 10,
        "id": 25,
        "label": "DEPENDED_ON",
        "properties": {
         "label": "DEPENDED_ON"
        },
        "start": 1,
        "thickness_factor": 1
       },
       {
        "color": "#9C27B0",
        "directed": true,
        "end": 11,
        "id": 26,
        "label": "CREATED_TITLE",
        "properties": {
         "label": "CREATED_TITLE"
        },
        "start": 1,
        "thickness_factor": 1
       },
       {
        "color": "#2196F3",
        "directed": true,
        "end": 12,
        "id": 27,
        "label": "SUCCEEDED_BY",
        "properties": {
         "label": "SUCCEEDED_BY"
        },
        "start": 1,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 12,
        "id": 28,
        "label": "PARENT",
        "properties": {
         "label": "PARENT"
        },
        "start": 13,
        "thickness_factor": 1
       },
       {
        "color": "#9E9E9E",
        "directed": true,
        "end": 14,
        "id": 29,
        "label": "DEPENDED_ON",
        "properties": {
         "label": "DEPENDED_ON"
        },
        "start": 1,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 17,
        "id": 40,
        "label": "WAR",
        "properties": {
         "label": "WAR"
        },
        "start": 16,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 18,
        "id": 41,
        "label": "WAR",
        "properties": {
         "label": "WAR"
        },
        "start": 16,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 19,
        "id": 42,
        "label": "WAR",
        "properties": {
         "label": "WAR"
        },
        "start": 16,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 3,
        "id": 43,
        "label": "WAR",
        "properties": {
         "label": "WAR"
        },
        "start": 16,
        "thickness_factor": 1
       },
       {
        "color": "#F44336",
        "directed": true,
        "end": 20,
        "id": 44,
        "label": "LEAD",
        "properties": {
         "label": "LEAD"
        },
        "start": 16,
        "thickness_factor": 1
       },
       {
        "color": "#F44336",
        "directed": true,
        "end": 21,
        "id": 45,
        "label": "LEAD",
        "properties": {
         "label": "LEAD"
        },
        "start": 16,
        "thickness_factor": 1
       },
       {
        "color": "#F44336",
        "directed": true,
        "end": 22,
        "id": 46,
        "label": "LEAD",
        "properties": {
         "label": "LEAD"
        },
        "start": 16,
        "thickness_factor": 1
       },
       {
        "color": "#F44336",
        "directed": true,
        "end": 23,
        "id": 47,
        "label": "LEAD",
        "properties": {
         "label": "LEAD"
        },
        "start": 16,
        "thickness_factor": 1
       },
       {
        "color": "#607D8B",
        "directed": true,
        "end": 24,
        "id": 48,
        "label": "DEFEAT",
        "properties": {
         "label": "DEFEAT"
        },
        "start": 16,
        "thickness_factor": 1
       },
       {
        "color": "#673AB7",
        "directed": true,
        "end": 12,
        "id": 64,
        "label": "SUCCESSOR",
        "properties": {
         "label": "SUCCESSOR"
        },
        "start": 1,
        "thickness_factor": 1
       },
       {
        "color": "#673AB7",
        "directed": true,
        "end": 7,
        "id": 65,
        "label": "SUCCESSOR",
        "properties": {
         "label": "SUCCESSOR"
        },
        "start": 5,
        "thickness_factor": 1
       },
       {
        "color": "#673AB7",
        "directed": true,
        "end": 29,
        "id": 66,
        "label": "SUCCESSOR",
        "properties": {
         "label": "SUCCESSOR"
        },
        "start": 5,
        "thickness_factor": 1
       },
       {
        "color": "#673AB7",
        "directed": true,
        "end": 1,
        "id": 67,
        "label": "SUCCESSOR",
        "properties": {
         "label": "SUCCESSOR"
        },
        "start": 5,
        "thickness_factor": 1
       },
       {
        "color": "#673AB7",
        "directed": true,
        "end": 30,
        "id": 68,
        "label": "SUCCESSOR",
        "properties": {
         "label": "SUCCESSOR"
        },
        "start": 5,
        "thickness_factor": 1
       },
       {
        "color": "#673AB7",
        "directed": true,
        "end": 31,
        "id": 69,
        "label": "SUCCESSOR",
        "properties": {
         "label": "SUCCESSOR"
        },
        "start": 5,
        "thickness_factor": 1
       },
       {
        "color": "#673AB7",
        "directed": true,
        "end": 32,
        "id": 70,
        "label": "SUCCESSOR",
        "properties": {
         "label": "SUCCESSOR"
        },
        "start": 5,
        "thickness_factor": 1
       },
       {
        "color": "#673AB7",
        "directed": true,
        "end": 33,
        "id": 71,
        "label": "SUCCESSOR",
        "properties": {
         "label": "SUCCESSOR"
        },
        "start": 5,
        "thickness_factor": 1
       },
       {
        "color": "#CDDC39",
        "directed": true,
        "end": 34,
        "id": 72,
        "label": "PREFERRED_SUCCESSOR",
        "properties": {
         "label": "PREFERRED_SUCCESSOR"
        },
        "start": 29,
        "thickness_factor": 1
       },
       {
        "color": "#9E9E9E",
        "directed": true,
        "end": 35,
        "id": 73,
        "label": "FAMILY_RELATION",
        "properties": {
         "label": "FAMILY_RELATION"
        },
        "start": 34,
        "thickness_factor": 1
       },
       {
        "color": "#9C27B0",
        "directed": true,
        "end": 13,
        "id": 74,
        "label": "MARRIAGE",
        "properties": {
         "label": "MARRIAGE"
        },
        "start": 35,
        "thickness_factor": 1
       },
       {
        "color": "#2196F3",
        "directed": true,
        "end": 37,
        "id": 91,
        "label": "GRANDPARENT",
        "properties": {
         "label": "GRANDPARENT"
        },
        "start": 39,
        "thickness_factor": 1
       },
       {
        "color": "#2196F3",
        "directed": true,
        "end": 38,
        "id": 92,
        "label": "GRANDPARENT",
        "properties": {
         "label": "GRANDPARENT"
        },
        "start": 39,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 40,
        "id": 93,
        "label": "PARENT",
        "properties": {
         "label": "PARENT"
        },
        "start": 34,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 38,
        "id": 94,
        "label": "PARENT",
        "properties": {
         "label": "PARENT"
        },
        "start": 40,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 37,
        "id": 95,
        "label": "PARENT",
        "properties": {
         "label": "PARENT"
        },
        "start": 13,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 16,
        "id": 96,
        "label": "CONTENDER",
        "properties": {
         "label": "CONTENDER"
        },
        "start": 13,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 8,
        "id": 97,
        "label": "PARENT",
        "properties": {
         "label": "PARENT"
        },
        "start": 41,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 46,
        "id": 98,
        "label": "PARENT",
        "properties": {
         "label": "PARENT"
        },
        "start": 41,
        "thickness_factor": 1
       },
       {
        "color": "#4CAF50",
        "directed": true,
        "end": 47,
        "id": 99,
        "label": "PARENT",
        "properties": {
         "label": "PARENT"
        },
        "start": 41,
        "thickness_factor": 1
       },
       {
        "color": "#F44336",
        "directed": true,
        "end": 41,
        "id": 100,
        "label": "SPOUSE",
        "properties": {
         "label": "SPOUSE"
        },
        "start": 44,
        "thickness_factor": 1
       },
       {
        "color": "#F44336",
        "directed": true,
        "end": 42,
        "id": 101,
        "label": "SPOUSE",
        "properties": {
         "label": "SPOUSE"
        },
        "start": 44,
        "thickness_factor": 1
       },
       {
        "color": "#F44336",
        "directed": true,
        "end": 41,
        "id": 102,
        "label": "SPOUSE",
        "properties": {
         "label": "SPOUSE"
        },
        "start": 45,
        "thickness_factor": 1
       },
       {
        "color": "#F44336",
        "directed": true,
        "end": 48,
        "id": 103,
        "label": "SPOUSE",
        "properties": {
         "label": "SPOUSE"
        },
        "start": 46,
        "thickness_factor": 1
       },
       {
        "color": "#F44336",
        "directed": true,
        "end": 51,
        "id": 117,
        "label": "HELD_TITLE",
        "properties": {
         "label": "HELD_TITLE"
        },
        "start": 50,
        "thickness_factor": 1
       }
      ],
      "_graph_layout": {},
      "_highlight": [],
      "_license": {},
      "_model_module": "yfiles-jupyter-graphs",
      "_model_module_version": "^1.6.1",
      "_model_name": "GraphModel",
      "_neighborhood": {},
      "_nodes": [
       {
        "color": "#2196F3",
        "id": 1,
        "label": "Elizabeth I",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Elizabeth I",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#4CAF50",
        "id": 2,
        "label": "England",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "England",
         "label": "Country:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#4CAF50"
       },
       {
        "color": "#4CAF50",
        "id": 3,
        "label": "Ireland",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Ireland",
         "label": "Country:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#4CAF50"
       },
       {
        "color": "#F44336",
        "id": 4,
        "label": "House Of Tudor",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "House Of Tudor",
         "label": "__Entity__:Royal family"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#F44336"
       },
       {
        "color": "#2196F3",
        "id": 5,
        "label": "Henry Viii",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Henry Viii",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 6,
        "label": "Anne Boleyn",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Anne Boleyn",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 7,
        "label": "Edward Vi",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Edward Vi",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 8,
        "label": "Lady Jane Grey",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Lady Jane Grey",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 9,
        "label": "Mary",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Mary",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 10,
        "label": "William Cecil",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "William Cecil",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#607D8B",
        "id": 11,
        "label": "Baron Burghley",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Baron Burghley",
         "label": "Title:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#607D8B"
       },
       {
        "color": "#2196F3",
        "id": 12,
        "label": "James Vi Of Scotland",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "James Vi Of Scotland",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 13,
        "label": "Mary, Queen Of Scots",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Mary, Queen Of Scots",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 14,
        "label": "Francis Walsingham",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Francis Walsingham",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 16,
        "label": "Elizabeth",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Elizabeth",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#4CAF50",
        "id": 17,
        "label": "Spain",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Spain",
         "label": "Country:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#4CAF50"
       },
       {
        "color": "#4CAF50",
        "id": 18,
        "label": "Netherlands",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Netherlands",
         "label": "Country:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#4CAF50"
       },
       {
        "color": "#4CAF50",
        "id": 19,
        "label": "France",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "France",
         "label": "Country:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#4CAF50"
       },
       {
        "color": "#673AB7",
        "id": 20,
        "label": "William Shakespeare",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "William Shakespeare",
         "label": "Playwright:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#673AB7"
       },
       {
        "color": "#673AB7",
        "id": 21,
        "label": "Christopher Marlowe",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Christopher Marlowe",
         "label": "Playwright:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#673AB7"
       },
       {
        "color": "#CDDC39",
        "id": 22,
        "label": "Francis Drake",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Francis Drake",
         "label": "Explorer:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#CDDC39"
       },
       {
        "color": "#CDDC39",
        "id": 23,
        "label": "Walter Raleigh",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Walter Raleigh",
         "label": "Explorer:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#CDDC39"
       },
       {
        "color": "#9E9E9E",
        "id": 24,
        "label": "Spanish Armada",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Spanish Armada",
         "label": "Event:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#9E9E9E"
       },
       {
        "color": "#2196F3",
        "id": 29,
        "label": "Mary I",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Mary I",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 30,
        "label": "Jane Grey",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Jane Grey",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 31,
        "label": "Katherine Grey",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Katherine Grey",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 32,
        "label": "Mary Grey",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Mary Grey",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 33,
        "label": "Margaret Clifford",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Margaret Clifford",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 34,
        "label": "Margaret Douglas",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Margaret Douglas",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 35,
        "label": "Henry Stuart, Lord Darnley",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Henry Stuart, Lord Darnley",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 39,
        "label": "Margaret Tudor",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Margaret Tudor",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 37,
        "label": "James Vi",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "James Vi",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 38,
        "label": "Arbella Stuart",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Arbella Stuart",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 40,
        "label": "Charles Stuart",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Charles Stuart",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 41,
        "label": "Frances Grey",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Frances Grey",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 46,
        "label": "Lady Catherine Grey",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Lady Catherine Grey",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 47,
        "label": "Lady Mary Grey",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Lady Mary Grey",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 44,
        "label": "Charles Brandon",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Charles Brandon",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 42,
        "label": "Eleanor Clifford",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Eleanor Clifford",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 45,
        "label": "Henry Grey",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Henry Grey",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 48,
        "label": "Henry Herbert",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Henry Herbert",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#2196F3",
        "id": 50,
        "label": "Elizabeth Petrovna",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Elizabeth Petrovna",
         "label": "Person:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#2196F3"
       },
       {
        "color": "#607D8B",
        "id": 51,
        "label": "Empress Of Russia",
        "position": [
         0,
         0
        ],
        "properties": {
         "id": "Empress Of Russia",
         "label": "Title:__Entity__"
        },
        "scale_factor": 1,
        "size": [
         55,
         55
        ],
        "styles": {},
        "type": "#607D8B"
       }
      ],
      "_overview": {
       "enabled": null,
       "overview_set": false
      },
      "_selected_graph": [
       [],
       []
      ],
      "_sidebar": {
       "enabled": true,
       "start_with": ""
      },
      "_view_count": null,
      "_view_module": "yfiles-jupyter-graphs",
      "_view_module_version": "^1.6.1",
      "_view_name": "GraphView",
      "layout": "IPY_MODEL_9bac7003afd84cecb4e67a81a396ec8d"
     }
    },
    "9bac7003afd84cecb4e67a81a396ec8d": {
     "model_module": "@jupyter-widgets/base",
     "model_module_version": "1.2.0",
     "model_name": "LayoutModel",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": "800px",
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": "100%"
     }
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
